package org.emcode.sample.appleguice.server.handler;

import java.io.IOException;

import javax.jdo.PersistenceManager;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import net.customware.gwt.dispatch.server.ActionHandler;
import net.customware.gwt.dispatch.server.ExecutionContext;
import net.customware.gwt.dispatch.shared.ActionException;
import org.apache.commons.logging.Log;
import org.emcode.sample.appleguice.server.VisitorCounterFactory;
import org.emcode.sample.appleguice.server.PMF;
import org.emcode.sample.appleguice.server.ShardedCounter;
import org.emcode.sample.appleguice.shared.entity.Visitor;
import org.emcode.sample.appleguice.shared.rpc.SendGreeting;
import org.emcode.sample.appleguice.shared.rpc.SendGreetingResult;

import com.google.inject.Inject;
import com.google.inject.Provider;

public class SendGreetingHandler implements ActionHandler<SendGreeting, SendGreetingResult> {
	private final Log logger;
	private final Provider<ServletContext> servletContext;
	private final Provider<HttpServletRequest> servletRequest;
	private Integer visitorCount;

	@Inject
	public SendGreetingHandler(final Log logger,
				   final Provider<ServletContext> servletContext,				   
				   final Provider<HttpServletRequest> servletRequest) {
		this.logger = logger;
		this.servletContext = servletContext;
		this.servletRequest = servletRequest;
	}

	public SendGreetingResult execute(final SendGreeting action,
					  final ExecutionContext context) throws ActionException {
		final String name = action.getName();
		 
		try {
			
			
			String serverInfo = servletContext.get().getServerInfo();
			String userAgent = servletRequest.get().getHeader("User-Agent");
			final String message = "w00t, " + name + "! From " + serverInfo;
			logger.debug("Hitting SendGreetingHandler in the server");
			
			// ok, let's try to save it in the datastore
			Visitor v = new Visitor();
			v.setName(name);
			
			PersistenceManager pm = PMF.get().getPersistenceManager();
			logger.debug("Getting or creating sharded counter");
			ShardedCounter counter = getOrCreateCounter();
	        try {
	        	
			    // need to add shards; just always add one for every hit (for now)
	        	logger.debug("Adding a shard");
			    counter.addShard();
			    logCounts(counter);
			     
			    // now increment the counter
			    logger.debug("Incrementing the counter");
			    counter.increment();
			    logCount(counter);
			    
	        	logger.debug("Persisting visitor id:" + v.getId());
	    		pm.makePersistent(v);
	    		
	        } finally {
	            pm.close();
	        }
	        logger.debug("Saved visitor id:" + v.getId());
			
	        // now query for how many visitors
	        visitorCount = new Integer(counter.getCount());
	        
	        return new SendGreetingResult(name, message, visitorCount);
		}
		catch (Exception cause) {
			logger.error("Unable to send message", cause);
			
			throw new ActionException(cause);
		}
	}

	public void rollback(final SendGreeting action,
			     final SendGreetingResult result,
			     final ExecutionContext context) throws ActionException {
		// Nothing to do here
	}
	
	public Class<SendGreeting> getActionType() {
		return SendGreeting.class;
	}
	
	private ShardedCounter getOrCreateCounter() throws IOException {
	    VisitorCounterFactory factory = new VisitorCounterFactory();
	    ShardedCounter counter = factory.getCounter("visitor'\"counter");
	    if (counter == null) {
	      // Create a counter with 0 shards.
	      counter = factory.createCounter("visitor'\"counter");
	      // Add a first shard to the counter.
	      counter.addShard();
	      logger.info("No counter named 'visitor counter', so we created one");
	    }
	    return counter;
	}

	private void logCount(ShardedCounter counter) throws IOException {
		logger.info("Counter:" + counter.getCount());
	}
	
	private void logCounts(ShardedCounter counter) throws IOException {
		logCount(counter);
		logNumberOfShards(counter.getNumShards());
	}

	private void logNumberOfShards(int shards)throws IOException {
		logger.info("Number of shards:" + shards);
	}


	
}